package com.speedchina.common.download.utils;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.alibaba.excel.converters.AutoConverter;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.metadata.CellData;
import com.speedchina.common.download.base.CSVConfiguration;
import com.speedchina.common.download.convert.MyUrlImageConverter;
import com.speedchina.framework.utils.ReflectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.util.PropertiesUtil;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Csv导出工具类
 * @author xz
 */
public class CsvWriter {
	/** CSV文件列分隔符 */
	private static final String CSV_COLUMN_SEPARATOR = ",";
	/** CSV文件列分隔符 */
	private static final String CSV_RN = "\r\n";

	/**
	 * 下载csv数据
	 * @Author xz
	 * @Param [data, clazz, fileName, response]
	 * @return void
	 */
	public static void downloadCsv(HttpServletResponse response,List data,Class clazz,String fileName) {
		String content = formatCsvData(data, clazz);
		try {
			exportCsv(fileName,content,response);
		} catch (IOException e) {

		}
	}

	/**
     * 获取到输出字符串
	 * @author: xz
	 * @date:  2021/5/17
	 * [data, displayColNames, matchColNames]
	 * @return java.lang.String
	 * @throws
	 */
	public static String formatCsvData(List data, String displayColNames, String matchColNames){
		StringBuffer buf = new StringBuffer();
		String[] displayColNamesArr = null;
		String[] matchColNamesMapArr = null; 
		displayColNamesArr = displayColNames.split(CSV_COLUMN_SEPARATOR);
		matchColNamesMapArr = matchColNames.split(CSV_COLUMN_SEPARATOR);
		// 输出列头
		for (int i = 0; i < displayColNamesArr.length; i++) {
			buf.append(displayColNamesArr[i]).append(CSV_COLUMN_SEPARATOR);
		}
		buf.append(CSV_RN); 
		if (null != data) {
			List<Field> filed = getFiled(data.get(0).getClass(), matchColNamesMapArr);
			// 输出数据
			for (int i = 0; i < data.size(); i++) {
				for (int j = 0; j < filed.size(); j++) {
				    //获取到需要输出的值.
					Object value = ReflectionUtils.getProperty(data.get(i), filed.get(j).getName());
					if ( filed.get(j).isAnnotationPresent(ExcelProperty.class)) {
						Converter converter = null;
						try {
							converter =  filed.get(j).getAnnotation(ExcelProperty.class).converter().newInstance();
							CellData cellData = converter.convertToExcelData(String.valueOf(value), null, null);
							if (cellData != null) {
								value = cellData.getStringValue();
							}
						} catch (InstantiationException e) {
							e.printStackTrace();
						} catch (IllegalAccessException e) {
							e.printStackTrace();
						} catch (Exception e) {
							e.printStackTrace();
						}

					}
					//根据属性获取 value值
                    buf.append(value).append(CSV_COLUMN_SEPARATOR);
				}
				buf.append(CSV_RN);
			}
		}
		return buf.toString();
	}

	/**
	 * 用了excel的注解配置的 表头名，index
	 * @author: xz
	 * @date:  2021/5/18
	 * [data, clazz]
	 * @return java.lang.String
	 * @throws
	 */
	public static String formatCsvData(List data,Class clazz){
		StringBuffer buf = new StringBuffer();
		List<CSVConfiguration> csvConfiguration = getCSVConfiguration(clazz);
		// 输出列头
		for (int i = 0; i < csvConfiguration.size(); i++) {
			buf.append(csvConfiguration.get(i).getHead()).append(CSV_COLUMN_SEPARATOR);
		}
		buf.append(CSV_RN);
		if (null != data) {
			// 输出数据
			for (int i = 0; i < data.size(); i++) {
				//处理每一个字段属性
				for (CSVConfiguration configuration : csvConfiguration) {
					//获取到当前属性字段
					Field field = configuration.getField();
					//反射获取到属性的值
					Object value = ReflectionUtils.getProperty(data.get(i), field.getName());
					if (value != null){
						//获取到转换器
						Converter converter = configuration.getConverter();
						if (!AutoConverter.class.isInstance(converter)){
							//获取到的不是默认转换器
							CellData cellData = null;
							try {
								cellData = converter.convertToExcelData(String.valueOf(value), null, null);
							} catch (Exception e) {
								e.printStackTrace();
							}
							if (cellData != null) {
								value = cellData.getStringValue();
							}
						}
					}
					//根据属性获取 value值
					buf.append(format(value,configuration.getDataFormat())).append(CSV_COLUMN_SEPARATOR);
				}
				buf.append(CSV_RN);
			}
		}
		return buf.toString();
	}


 	public static void exportCsv(String fileName, String content, HttpServletResponse response) throws IOException {
		//生成文件名
		if (StringUtils.isBlank(fileName)){
			SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddhh24mmss");
			fileName = sdf.format(new Date());
		}
		// 读取字符编码
		response.setContentType("application/octet-stream");
		response.setCharacterEncoding("utf-8");
		// 这里URLEncoder.encode可以防止中文乱码
		fileName = URLEncoder.encode(fileName , "UTF-8");
		response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".csv");
		// 写出响应
		OutputStream os = response.getOutputStream();
		os.write(content.getBytes("GBK"));
		os.flush();
		os.close();
	}

    /**
	 * 获取到 matchColNamesMapArr 对应的属性
     * @author: xz
     * @date:  2021/5/18
     * [clazz, matchColNamesMapArr]
     * @return java.util.List<java.lang.reflect.Field>
     * @throws
     */
    private static List<Field> getFiled(Class clazz,String[] matchColNamesMapArr){
		List<Field> list = new ArrayList<>();
		List<Field> allFields = ReflectionUtils.getAllFields(clazz);
		for (String s : matchColNamesMapArr) {
			for (Field field : allFields) {
				if (s.equals(field.getName())) {
					list.add(field);
				}
			}
		}
		return list;
	}

	/**
	 * 获取easyExcel的注解配置，用作与csv的导出
	 * @author: xz
	 * @date:  2021/5/18
	 * [clazz]
	 * @return java.util.List<com.speedchina.common.download.base.CSVConfiguration>
	 * @throws
	 */
	public static List<CSVConfiguration> getCSVConfiguration(Class clazz){
		List<CSVConfiguration> list = new ArrayList<>();
		//获取到所有的属性
		List<Field> allFields = ReflectionUtils.getAllFields(clazz);
		for (Field field : allFields) {
			//属性上标有注解说明要写如
			if (field.isAnnotationPresent(ExcelProperty.class)){
				CSVConfiguration csvConfiguration = new CSVConfiguration();
				//
				if (field.getType() == Date.class){
					//获取到时间格式化字符串
					csvConfiguration.setDataFormat(field.getAnnotation(DateTimeFormat.class).value());
				}
				//标注有此注解，则获取到其属性配置，获取表头
				String[] value = field.getAnnotation(ExcelProperty.class).value();
				//获取表头顺序
				int index = field.getAnnotation(ExcelProperty.class).index();
				Converter converter = null;
				try {
					//获取到转换器类
					converter = field.getAnnotation(ExcelProperty.class).converter().newInstance();
				} catch (InstantiationException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}
				//存入实体载体
				csvConfiguration.setField(field).setHead(value[0]).setOrder(index).setConverter(converter);
				list.add(csvConfiguration);
			}
		}
		//放入后利用index进行表头排序 编号小的在前面
		return list.stream().distinct().sorted(new Comparator<CSVConfiguration>() {
			@Override
			public int compare(CSVConfiguration o1, CSVConfiguration o2) {
				return o1.getOrder() - o2.getOrder();
			}
		}).collect(Collectors.toList());
	}

	/**
	 * 对数据进行处理，防止数据到excel中显示科学计数，特殊字符 ，时间格式化
	 * @author: hqing
	 * @date:  2021/5/19
	 * [value, fmt]
	 * @return java.lang.String
	 * @throws
	 */
	public static String format(Object value, String fmt) {
		if(value == null) {
			return "";
		}else if(value instanceof String && ((String) value).indexOf(CSV_COLUMN_SEPARATOR) > 0) {
			//处理逗号
			return new StringBuilder("\"").append(value).append("\"").toString();
		}else if(value instanceof Date) {
			return new SimpleDateFormat(StringUtils.isEmpty(fmt) ? "yyyy-MM-dd HH:mm:ss" : fmt).format(value);
		}else if(value instanceof BigDecimal || value instanceof Integer || value instanceof Double || value instanceof Float || value instanceof Long) {
			// 保留数字类型， + \t 转成了字符串 无法满足前端 自定义处理的需求
			return "=" + value.toString();
			// return value.toString() + "\t";
		}
		return value.toString() + "\t";
	}

}